package cn.edu.sjtu.se.dslab.rhapsody.parse;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.Node;
import org.dom4j.QName;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

import cn.edu.sjtu.se.dslab.util.FileUtil;

public class XmiParser {
	private static final Logger logger = Logger.getLogger(XmiParser.class);

	private Document document;
	private XmiBean xmiBean;
	
	private Map<String, String> namespaceMap = new HashMap<String, String>();

	public XmiParser(String path, XmiBean xmiBean) {
		this.xmiBean = xmiBean;

		path = FileUtil.getRootPath() + path;
		SAXReader reader = new SAXReader();
		try {
			document = reader.read(new File(path));
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	public XmiBean parse() {
		if (document == null)
			return xmiBean;
		readXmiBean();
		return xmiBean;
	}

	private void readXmiBean() {
		Element rootElement = document.getRootElement();
		//初始化命名空间
		initNameSpace(rootElement);
		
//		
//		XPath xpath= document.createXPath("//Functional:DataZone");
//		xpath.setNamespaceURIs(namespaceMap);
//		List<Node> nodes = xpath.selectNodes(document);
//		for (Node n : nodes){
//			logger.info(n.getName());
//			logger.info(n.getText());
//		}

		//获取根元素下的uml:model元素
		List<Element> umlModels = rootElement.elements(XmiConst.L_UML_MODEL); // full quanlified name
		logger.info("Parsing " + XmiConst.L_UML_MODEL + " total:" + umlModels.size());
		for (Element e : umlModels) {
			UmlModel umlModel = readUmlModel(e);
			if (umlModel != null) {
				umlModel.setId(e.attributeValue(XmiConst.A_XMI_ID));
				umlModel.setName(e.attributeValue(XmiConst.A_NAME));
				xmiBean.getUmlModels().add(umlModel);
			}
		}
		// read profiles
		//解析根元素下的Architecture:OuterSystem
		List<Element> externalSystems = rootElement
				.elements(XmiConst.L_ARCHITECTURE_OUTERSYSTEM);
		logger.info("Parsing "	+ XmiConst.L_ARCHITECTURE_OUTERSYSTEM.getQualifiedName() + " total:" + externalSystems.size());
		for (Element e : externalSystems) {
			ProfileType pt = new ProfileType();
			pt.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			pt.setLabel(XmiConst.L_ARCHITECTURE_OUTERSYSTEM.getQualifiedName());
			String busType = e.attributeValue(XmiConst.A_BUS_TYPE);
			if (busType != null)
				pt.getProperties().put(XmiConst.A_BUS_TYPE, busType);
			String baseClass = e.attributeValue(XmiConst.A_BASE_CLASS);
			if (baseClass != null)
				pt.getProperties().put(XmiConst.A_BASE_CLASS, baseClass);
			String baseActor = e.attributeValue(XmiConst.A_BASE_ACTOR);
			if (baseActor != null)
				pt.getProperties().put(XmiConst.A_BASE_ACTOR, baseActor);

			// for (Entry<String, String> entry: pt.getProperties().entrySet()){
			// System.out.println("\t[" + entry.getKey() + "]=" +
			// entry.getValue());
			// }
			xmiBean.getProfileTypes().add(pt);
		}

		List<Element> dataZones = rootElement
				.elements(XmiConst.L_FUNCTIONAL_DATAZONE);
		logger.info("Parsing "
				+ XmiConst.L_FUNCTIONAL_DATAZONE.getQualifiedName() + " total:"
				+ dataZones.size());
		for (Element e : dataZones) {
			ProfileType pt = new ProfileType();
			pt.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			pt.setLabel(XmiConst.L_FUNCTIONAL_DATAZONE.getQualifiedName());
			String uri = e.attributeValue(XmiConst.A_URI);
			if (uri != null)
				pt.getProperties().put(XmiConst.A_URI, uri);
			String zoneType = e.attributeValue(XmiConst.A_ZONE_TYPE);
			if (zoneType != null)
				pt.getProperties().put(XmiConst.A_ZONE_TYPE, zoneType);
			String size = e.attributeValue(XmiConst.A_SIZE);
			if (size != null)
				pt.getProperties().put(XmiConst.A_SIZE, size);
			String baseProperty = e.attributeValue(XmiConst.A_BASE_PROPERTY);
			if (baseProperty != null)
				pt.getProperties().put(XmiConst.A_BASE_PROPERTY, baseProperty);
			
			String entityUri = e.attributeValue(XmiConst.A_ENTITYURI);
			if (entityUri != null)
				pt.getProperties().put(XmiConst.A_ENTITYURI, entityUri);
			
			xmiBean.getProfileTypes().add(pt);
		}
		//event responser
		List<Element> eventResponsers = rootElement
				.elements(XmiConst.L_FUNCTIONAL_EVENTRESPONSER);
		logger.info("Parsing "
				+ XmiConst.L_FUNCTIONAL_EVENTRESPONSER.getQualifiedName() + " total:"
				+ eventResponsers.size());
		for (Element e : eventResponsers) {
			ProfileType pt = new ProfileType();
			pt.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			pt.setLabel(XmiConst.L_FUNCTIONAL_EVENTRESPONSER.getQualifiedName());
			String eventUri = e.attributeValue(XmiConst.A_EVENTURI);
			if (eventUri != null)
				pt.getProperties().put(XmiConst.A_EVENTURI, eventUri);
			String pageUri = e.attributeValue(XmiConst.A_PAGEURI);
			if (pageUri != null)
				pt.getProperties().put(XmiConst.A_PAGEURI, pageUri);
			String baseOperation = e.attributeValue(XmiConst.A_BASE_OPERATION);
			if (baseOperation != null)
				pt.getProperties().put(XmiConst.A_BASE_OPERATION, baseOperation);
			xmiBean.getProfileTypes().add(pt);
		}
		
		// bus agents
		List<Element> busAgents = rootElement
				.elements(XmiConst.L_FUNCTIONAL_BUSINTERACTIONAGENT);
		logger.info("Parsing "
				+ XmiConst.L_FUNCTIONAL_BUSINTERACTIONAGENT.getQualifiedName()
				+ " total:" + busAgents.size());
		for (Element e : busAgents) {
			ProfileType pt = new ProfileType();
			pt.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			pt.setLabel(XmiConst.L_FUNCTIONAL_BUSINTERACTIONAGENT.getQualifiedName());
			//dataZones属性
			String datazones = e.attributeValue(XmiConst.A_DATAZONES);
			if (datazones != null)
				pt.getProperties().put(XmiConst.A_DATAZONES, datazones);
			//outerSystemURI属性
			String outerSystems = e.attributeValue(XmiConst.A_OUTERSYSTEMURI);
			if (outerSystems != null)
				pt.getProperties().put(XmiConst.A_OUTERSYSTEMURI, outerSystems);
			//frequency 属性
			String frequency = e.attributeValue(XmiConst.A_FREQUENCY);
			if (frequency != null)
				pt.getProperties().put(XmiConst.A_FREQUENCY, frequency);
			//base_Operation属性
			String baseOperation = e.attributeValue(XmiConst.A_BASE_OPERATION);
			if (baseOperation != null)
				pt.getProperties()
						.put(XmiConst.A_BASE_OPERATION, baseOperation);
			xmiBean.getProfileTypes().add(pt);
		}
		
		// display agents
		List<Element> displayAgent = rootElement
				.elements(XmiConst.L_FUNCTIONAL_INFORMATIONDISPLAYAGENT);
		logger.info("Parsing " + XmiConst.L_FUNCTIONAL_INFORMATIONDISPLAYAGENT.getQualifiedName()
				+ " total:" + displayAgent.size());
		for (Element e : displayAgent) {
			ProfileType pt = new ProfileType();
			pt.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			pt.setLabel(XmiConst.L_FUNCTIONAL_INFORMATIONDISPLAYAGENT.getQualifiedName());
			String datazones = e.attributeValue(XmiConst.A_DATAZONES);
			logger.info(datazones);
			if (datazones != null)
				pt.getProperties().put(XmiConst.A_DATAZONES, datazones);
			String pageUri = e.attributeValue(XmiConst.A_PAGEURI);
			if (pageUri != null)
				pt.getProperties().put(XmiConst.A_PAGEURI, pageUri);
			String baseOperation = e.attributeValue(XmiConst.A_BASE_OPERATION);
			if (baseOperation != null)
				pt.getProperties().put(XmiConst.A_BASE_OPERATION, baseOperation);
			xmiBean.getProfileTypes().add(pt);
		}
		
		//domainEntity
		List<Element> domainEntitys = rootElement.elements(XmiConst.L_FUNCTIONAL_DOMAINENTITY);
		logger.info(XmiConst.L_FUNCTIONAL_DOMAINENTITY.getNamespace().getName());
		logger.info(XmiConst.L_FUNCTIONAL_DOMAINENTITY.getNamespacePrefix());
		logger.info(XmiConst.L_FUNCTIONAL_DOMAINENTITY.getNamespaceURI());
		logger.info("Parsing " + XmiConst.L_FUNCTIONAL_DOMAINENTITY.getQualifiedName()
				+ " total:" + domainEntitys.size());
		for (Element e : domainEntitys) {
			ProfileType pt = new ProfileType();
			pt.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			pt.setLabel(XmiConst.L_FUNCTIONAL_DOMAINENTITY.getQualifiedName());
			String entityUri = e.attributeValue(XmiConst.A_ENTITYURI);
			if (entityUri != null)
				pt.getProperties().put(XmiConst.A_ENTITYURI, entityUri);
			String entityType = e.attributeValue(XmiConst.A_ENTITY_TYPE);
			if (entityType != null)
				pt.getProperties().put(XmiConst.A_ENTITY_TYPE, entityType);
			//对应与data_entity的属性
			String baseClass = e.attributeValue(XmiConst.A_BASE_CLASS);
			if (baseClass != null)
				pt.getProperties().put(XmiConst.A_BASE_CLASS, baseClass);
			//对应与data_type的属性
			String baseDataType = e.attributeValue(XmiConst.A_BASE_DATATYPE);
			if (baseDataType != null)
				pt.getProperties().put(XmiConst.A_BASE_DATATYPE, baseDataType);
			
			xmiBean.getProfileTypes().add(pt);
		}

	}
	
	private void initNameSpace(Element rootElement){
		//获取所有声明的命名空间
		List<Namespace> namespaces = rootElement.declaredNamespaces();
		for(Namespace ns : namespaces){
			logger.info("NameSpace Prefix:" + ns.getPrefix());
			logger.info("NameSpace URI:" + ns.getURI());
			
			//前缀为Architecture的标签
			if (ns.getPrefix().equals("Architecture")){
				XmiConst.ARCHITECTURE_NS = new Namespace(ns.getPrefix(), ns.getURI());
				//Architecture:OuterSystem
				XmiConst.L_ARCHITECTURE_OUTERSYSTEM = new QName("OuterSystem", XmiConst.ARCHITECTURE_NS);
				
			}
			//前缀为Functional的标签，例如
			if (ns.getPrefix().equals("Functional")){
				XmiConst.FUNCTIONAL_NS = new Namespace(ns.getPrefix(), ns.getURI());
				//Functional:DataZone
				XmiConst.L_FUNCTIONAL_DATAZONE = new QName("DataZone", XmiConst.FUNCTIONAL_NS);
				//Functional:EventResponser
				XmiConst.L_FUNCTIONAL_EVENTRESPONSER = new QName("EventResponser", XmiConst.FUNCTIONAL_NS);
				//Functional:BusInteractionAgent
				XmiConst.L_FUNCTIONAL_BUSINTERACTIONAGENT = new QName("BusInteractionAgent", XmiConst.FUNCTIONAL_NS);
				//Functional:InformationDisplayAgent
				XmiConst.L_FUNCTIONAL_INFORMATIONDISPLAYAGENT = new QName("InformationDisplayAgent", XmiConst.FUNCTIONAL_NS);
				//Functional:DomainEntity
				XmiConst.L_FUNCTIONAL_DOMAINENTITY = new QName("DomainEntity", XmiConst.FUNCTIONAL_NS);
			}

		}
	}

	private UmlModel readUmlModel(Element e) {
		// now just parse the packagedElement
		//解析UmlModel下的packagedElement标签的元素
		List<Element> elements = e.elements(XmiConst.L_PACKAGED_ELEMENT);
		logger.info("Parsing " + XmiConst.L_PACKAGED_ELEMENT + " size:"	+ elements.size());
		UmlModel umlModel = null;
		if (elements != null && elements.size() != 0) {
			umlModel = new UmlModel();
			for (Element element : elements) {
				// logger.info(element.attributeValue(XmiConst.A_XMI_TYPE) +
				// XmiConst.T_UML_PACKAGE.getQualifiedName());
				//获取packagedElement的xmi:type属性
				String aType = element.attributeValue(XmiConst.A_XMI_TYPE); // get the value of xmi:type
				// primitive type , package
				if (XmiConst.UML_TYPE_SET.get(aType) != null) {
					XmiType xmiType = readXmiType(element);
					if (xmiType != null) {
						umlModel.getChildren().add(xmiType);
					}
				}
			}
		}
		return umlModel;
	}

	private XmiType readXmiType(Element e) {
		//获取packagedElement的xmi:type属性
		String xmiTypeValue = e.attributeValue(XmiConst.A_XMI_TYPE);
		//logger.info("Parsing " + xmiTypeValue);
		
		//xmiTypeValue是xmiType的值，例如uml:Property
		//infoMap中记录了所有出现过的xmiType，把xmiType作为key，然后value是一个以这些元素的名字为key的map
		Map<String, Map<String, XmiType>> infoMap = xmiBean.getInfoMap();
		Map<String, XmiType> idMap = xmiBean.getIdMap();

		XmiType xmiType = null;
		//根据不同的xmi:type进行解析
		if (xmiTypeValue.equals(XmiConst.T_UML_PACKAGE.getQualifiedName())) {
			//logger.info("package");
			xmiType = readXmiComposite(e); //包含子元素
		} else if (xmiTypeValue.equals(XmiConst.T_UML_CLASS.getQualifiedName())) {
			//logger.info("class");
			xmiType = readXmiComposite(e); //包含子元素
		} else if (xmiTypeValue.equals(XmiConst.T_UML_PRIMITIVETYPE.getQualifiedName())) {
			//logger.info("primitive type");
			xmiType = readXmiUnit(e);
		} else if (xmiTypeValue.equals(XmiConst.T_UML_PROPERTY.getQualifiedName())) {
			//logger.info("property");
			xmiType = readXmiUnit(e);
		}else if (xmiTypeValue.equals(XmiConst.T_UML_OPERATION.getQualifiedName())) {
			//logger.info("Operation");
			xmiType = readXmiUnit(e);
		}

		if (null != xmiType) {
			xmiType.setName(e.attributeValue(XmiConst.A_NAME));
			xmiType.setXmiId(e.attributeValue(XmiConst.A_XMI_ID));
			xmiType.setXmiType(xmiTypeValue);
			xmiType.setLabel(e.getQualifiedName());
			//logger.info(XmiConst.A_NAME + "=" + xmiType.getName());
			//logger.info(XmiConst.A_XMI_ID.getQualifiedName() + "="
			//		+ xmiType.getXmiId());
			//logger.info(XmiConst.A_XMI_TYPE.getQualifiedName() + "="
			//		+ xmiType.getXmiType());
			
			//xmiTypeValue是xmiType的值，例如uml:Property
			if (infoMap.get(xmiTypeValue) == null) {
				Map<String, XmiType> typeMap = new HashMap<String, XmiType>();
				infoMap.put(xmiTypeValue, typeMap);
			}
			if (xmiType.getName() != null) {
				infoMap.get(xmiTypeValue).put(xmiType.getName(), xmiType); // name as key
				idMap.put(xmiType.getXmiId(), xmiType);  //id as key
			}
		}
		return xmiType;
	}

	private XmiComposite readXmiComposite(Element e) {
		XmiComposite composite = null;
		List<Element> elements = e.elements();
		if (elements == null)
			return composite;
		composite = new XmiComposite();
		for (Element element : elements) {
			XmiType xmiType = readXmiType(element);
			if (xmiType != null) {
				composite.getChildren().add(xmiType);
			}
		}
		return composite;
	}

	private XmiUnit readXmiUnit(Element e) {
		XmiUnit unit = null;
		//deal with property
		if (XmiConst.T_UML_PROPERTY.getQualifiedName().equals(
				e.attributeValue(XmiConst.A_XMI_TYPE))) {
			unit = new XmiUnit();
			Map<String ,String> properties = unit.getProperties();
			List<Attribute> atts = e.attributes();
			for (Attribute a : atts){
				//System.out.println("++++++++++++" + a.getQualifiedName() + "---" + a.getValue());
				properties.put(a.getQualifiedName(), a.getValue());
			}
			
//			String v = e.attributeValue(XmiConst.A_VISIBILITY);
//			if (v != null) {
//				logger.info(">>>>>>>>>>>>>>>>>>> " + v);
//				properties.put(XmiConst.A_VISIBILITY, v);
//			}
//			v = e.attributeValue(XmiConst.A_AGGREGATION);
//			if ( v!= null) {
//				logger.info(">>>>>>>>>>>>>>>>>>> " + v);
//				properties.put(XmiConst.A_AGGREGATION, v);
//			}
//			v = e.attributeValue(XmiConst.A_TYPE);
//			if ( v!= null) {
//				logger.info(e.attribute(XmiConst.A_TYPE));
//				logger.info(e.attribute(XmiConst.A_XMI_TYPE));
//				logger.info(">>>>>>>>>>>>>>>>>>> " + v);
//				properties.put(XmiConst.A_TYPE, v);
//			}
		}// deal with primitive type
		else if (XmiConst.T_UML_PRIMITIVETYPE.getQualifiedName().equals(
				e.attributeValue(XmiConst.A_XMI_TYPE))) {
			unit = new XmiUnit();
			Map<String ,String> properties = unit.getProperties();
			List<Attribute> atts = e.attributes();
			for (Attribute a : atts){
				//System.out.println("++++++++++++" + a.getQualifiedName() + "---" + a.getValue());
				properties.put(a.getQualifiedName(), a.getValue());
			}
		}else if (XmiConst.T_UML_OPERATION.getQualifiedName().equals(
				e.attributeValue(XmiConst.A_XMI_TYPE))) {
			unit = new XmiUnit();
			Map<String ,String> properties = unit.getProperties();
			List<Attribute> atts = e.attributes();
			for (Attribute a : atts){
				properties.put(a.getQualifiedName(), a.getValue());
			}
		}
		return unit;
	}
}
